
import java.io.File;
import java.io.FileNotFoundException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.util.ArrayList;


public class index {

	public long totalSize;
	public long actualSize;
	public long nextpointer;
	public long freelist;
	public long blockLength;
	public File fo = new File("");
	public File ft;
	public long endpoint; 
	int ram =0;
	datatype[] datatypes = new datatype[7];
	public index()
	{

	}
	public index(long temp[])
	{
		totalSize = temp[0];
		actualSize = temp[1];
		nextpointer = temp[2];
		freelist = temp[4];
		blockLength = temp[3];
		endpoint = temp[5];
		datatypes[0] = new i1();
		datatypes[1] = new i2();
		datatypes[2] = new i4();
		datatypes[3] = new i8();
		datatypes[4] = new r4();
		datatypes[5] = new r8();
		datatypes[6] = new cx();
	}
	
	public String toString()
	{
		return totalSize+"\t"+actualSize+"\t"+nextpointer+"\t"+blockLength+"\t"+freelist+"\t"+endpoint+"\t";
	}
	public void updateIndex(String data, long rid, ArrayList<datatype> header, int i)
	{
		try{

			//datatype d= new datatype();
			i8 te = new i8();
			Object o = datatypes[header.get(i).type].convert(data);
			long bid =  calculateBucket(datatypes[header.get(i).type].hashCode(o));
			byte [] ridByte = te.returnPrimitive(""+rid);
			byte [] dataByte = datatypes[header.get(i).type].returnPrimitive(data);
			Bucket bucket = getBucket(bid, ft);
			boolean primary = true;

			if(bucket.offset!=Long.MAX_VALUE)
			{
				primary = false;
				while(bucket.offset!= Long.MAX_VALUE)
				{
					bid = bucket.offset;
					Bucket bucket1 = getBucket(bid, fo);
					bucket = bucket1;
				}
			}
			File r;
			if(primary)
				r= this.ft;
			else
				r= this.fo;
			long empty = isBucketEmpty(bid, r);
			;
			if(empty == -1 || empty == -2)
			{
				long freeBucket = getfreeBucket();
				bucket.offset = freeBucket;
				writeBucket(bucket, r, bid);
				Bucket New = new  Bucket();
				for(int s=0;s<ridByte.length;s++)
					New.data[s] = ridByte[s];
				for(int s=0;s<dataByte.length;s++)
					New.data[8+s] = dataByte[s];
				if(8+dataByte.length<New.data.length-1)
					New.data[8+dataByte.length] = '`';
				writeBucket(New, fo, freeBucket);

				rehash(header,i);			
			}
			else
			{
				for(int s=0;s<ridByte.length;s++)
					bucket.data[(int)empty+s] = ridByte[s];
				for(int s=0;s<dataByte.length;s++)
					bucket.data[(int)empty+8+s] = dataByte[s];
				if(empty+8+dataByte.length< bucket.data.length-1)
					bucket.data[(int)empty+8+dataByte.length] = '`';
				writeBucket(bucket, r, bid);
			}


		}
		catch (Exception e) 
		{
			e.printStackTrace();
		}

	}
	public Bucket getBucket(long bid, File f)
	{
		try
		{
			long pos=0;
			if(f.equals(fo))
				pos = 48;
			RandomAccessFile fi2 = new RandomAccessFile(f, "rw");
			fi2.seek(pos+bid* Bucket.bucketLength);
			byte read[] = new byte[Bucket.bucketLength];
			fi2.read(read);
			fi2.close();
			Bucket b = new Bucket(read);
			return b;
		}
		catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}
	public void writeBucket(Bucket b, File f, long pos)
	{
		try
		{
			long p = 0 ;
			if(f.equals(fo))
				p = 48;
			RandomAccessFile fi = new RandomAccessFile(f, "rw");
			fi.seek(p+ pos*b.bucketLength);
			fi.write(b.data);
			fi.writeLong(b.offset);
			fi.close();

		}
		catch(Exception e)
		{

		}
	}
	public long isBucketEmpty(long bid, File write)
	{
		try
		{
			RandomAccessFile fi = new RandomAccessFile(write, "rw");
			long pos=0;
			if(write.equals(fo))
			{
				pos=48;
			}
			else
			{	pos=0;

			}
			long bucketpos = pos+bid*Bucket.bucketLength;
			long bytepos = bucketpos;
			while(bytepos + this.blockLength <= (bucketpos+Bucket.bucketLength-8))
			{
				fi.seek(bytepos);
				byte read=fi.readByte();
				if((char)read =='`')
				{
					fi.close();
					return bytepos-bucketpos;
				}
				else
					bytepos = bytepos + this.blockLength;
			}
			fi.close();
			return -1;
		}
		catch(Exception e)
		{
			return -2;
		}
	}

	public long getfreeBucket()
	{

		long l=0;
		if(this.freelist!= Long.MAX_VALUE)
		{
			//System.out.println("BITCH \t" + ram++ +"\t" + freelist);
			l = freelist;
			Bucket b = getBucket(freelist, fo);
			freelist = b.offset;
			
			b.offset = Long.MAX_VALUE;
			b.data[0]=(byte)'`';
			writeBucket(b, fo, l);			
		}

		else
		{
			l=this.endpoint;
			Bucket x = new Bucket();
			x.data[0]=(byte)'`';
			writeBucket(x, fo, l);
			this.endpoint++;

		}
		return l;
	}

	public void rehash(ArrayList<datatype> header,int i)
	{
		//System.out.println(nextpointer + "\t" + actualSize + "\t" + totalSize);
		boolean primary = true;
		Bucket b=null;
		Bucket c=null;
		int cpointer=0;
		int dpointer=0;
		char frog = '`';
		if(primary)
		{
			b = getBucket(nextpointer, ft);
			c= new Bucket();
			c.data[0]=(byte)frog;
			writeBucket(c, ft, actualSize);
			primary =false;
			actualSize++;
		}
		Bucket d = new Bucket();
		d.data[0] = (byte) frog;
		writeBucket(d, ft, actualSize-1);
		long off =b.offset;
		boolean first1 = true;
		boolean first2 = true;
		boolean first3=true;
		long clocation = nextpointer;
		long dlocation = actualSize-1;
		long blocation = nextpointer;
		int m=0;
		while(!primary)
		{
			long s=0;
			byte[] block =  b.readBlock(m, this);
			if(block!=null)
			{
				byte[] cblock = new byte[(int)this.blockLength-8];

				for(int n=8;n<block.length;n++)
				{
					cblock[n-8]=block[n];
				}
				Object o= datatypes[header.get(i).type].getPrimitive(cblock); 
				int code = datatypes[header.get(i).type].hashCode(o);
				//System.out.println(o + "\t"+code);
				int rbucket=calculateBucket(code);
				//System.out.println(rbucket);
				if(rbucket==(int)nextpointer)
				{
					if(cpointer + blockLength <= c.data.length)
					{
						c.writeBlock(block, cpointer, this);
						cpointer += this.blockLength;
					}
					else
					{

						s = getfreeBucket();
						c.offset = s;
						if(first1)
						{
							writeBucket(c, ft, clocation);
							first1 = false;
						}
						else
						{
							writeBucket(c, fo, clocation);
						}
						c = getBucket(s, fo);
						clocation =s;
						cpointer = 0;
						c.writeBlock(block, cpointer, this);
						cpointer += this.blockLength;
					}

				}
				else if(rbucket == (int) actualSize-1)
				{

					if(dpointer + blockLength <= d.data.length)
					{
						d.writeBlock(block, dpointer, this);
						dpointer += this.blockLength;
					}
					else
					{

						s = getfreeBucket();
						d.offset = s;
						if(first2)
						{
							writeBucket(d, ft, dlocation);
							first2 = false;
						}
						else
						{
							writeBucket(d, fo, dlocation);
						}
						d = getBucket(s, fo);
						dlocation = s;
						dpointer = 0;
						d.writeBlock(block, dpointer, this);
						dpointer += this.blockLength;
					}
				}
				else
					System.out.println("Danger avoid");

				m += this.blockLength;

			}

			else
			{
				if(b.offset!=Long.MAX_VALUE)
				{
					if(first3)
					{
						b = getBucket(off, fo);
						first3 = false;
						blocation = off;
					}
					else
					{
						off = b.offset;
						b.offset = freelist;
						writeBucket(b, fo, blocation);
						freelist = blocation;
						b = getBucket(off, fo);
						blocation = off;
					}
					m=0;
				}
				else
				{
					if(first1)
					{
						writeBucket(c, ft, clocation);
						first1 = false;
					}
					else
					{
						writeBucket(c, fo, clocation);
					}
					if(first2)
					{
						writeBucket(d, ft, dlocation);
						first2 = false;
					}
					else
					{
						writeBucket(d, fo, dlocation);
					}
					primary = true;
				}

			}

		}

		nextpointer++;
		if(nextpointer == totalSize)
		{
			totalSize = 2*totalSize;
			nextpointer = 0;
		}

	}


	public int calculateBucket(int i)
	{
		i = i% (2*(int)totalSize);
		if(i>actualSize-1)
			i -=totalSize;
		return i;
	}

	public ArrayList<String> displayBucket(Bucket s, ArrayList<datatype> h, int l)
	{
		ArrayList<String> gas = new ArrayList<String>();
		int start =0;
		while(start <= s.data.length)
		{
			byte[] data = s.readBlock(start, this);
			byte[] cblock = new byte[(int)this.blockLength-8];
			byte[] dblock = new byte[8];
			if(data !=null)
			{
			for(int m = 0 ;m<8 ;m++)
				dblock [m] = data[m];
			for(int n=8;n<data.length;n++)
			{
				cblock[n-8]=data[n];
			}
				start += blockLength;
				Object o = datatypes[h.get(l).type].getPrimitive(cblock);
				i8 y = new i8();
				Object e = y.getPrimitive(dblock);
				gas.add(e+"");
				gas.add(o+"");
				//System.out.print(o+"\t") ;
			}
			else
			{
				return gas;
			}
		}
		return gas;
	}
	public void Showbuckets(int k, ArrayList<datatype> h, int l)
	{
		Bucket b = getBucket(k, ft);
		while(true)
		{
		displayBucket(b,h,l);
		System.out.println("====>");
		if(b.offset==Long.MAX_VALUE)
		break;
		else
			b = getBucket(b.offset, fo);
		}
	}

}
